/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       adv_garrison.cpp

	$Header: /game/adv_garrison.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adv_garrison.h"

#include "adv_object_type.h"
#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_sounds.h"
#include "ai_army_data_cache.h"
#include "army.h"
#include "army_info_window.h"
#include "combat_context_adv_object.h"
#include "combat_context_ptr.h"
#include "combat_result.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "player_color.h"
#include "run_combat.h"
#include "script_context_args.h"
#include "script_targeting.h"
#include "sized_int_types.h"
#include "stationary_adventure_object.h"
#include "streambuf_operators.h"
#include "town_image_level.h"

// ---------------------------------------------------------------
// unnamed namespace
// ---------------------------------------------------------------

namespace
{

	int const k_current_save_game_version = 6;
	t_object_registration< t_adv_garrison > g_garrison_registration( k_adv_object_garrison );

	// Convert t_adv_garrison's save file version to t_ownable_garrisonable_adv_object's save file version
	inline int to_base_version( int format_version )
	{
		if ( format_version <= 1 )
			return 0;
		else if ( format_version == 2 )
			return 1;
		else if ( format_version == 3 )
			return 2;
		else if ( format_version == 4 )
			return 3;
		else
			return 4;
	}

} // unnamed namespace


// ---------------------------------------------------------------
// garrison adventure object
// ---------------------------------------------------------------
int t_adv_garrison::get_version() const
{
	return k_current_save_game_version;
}


// ---------------------------------------------------------------
// garrison adventure object
// ---------------------------------------------------------------
bool t_adv_garrison::read( std::streambuf&						stream, 
						   t_qualified_adv_object_type const&	type,
						   int									version )
{
	if (version < 1)
		return true;

	if (version < 6)
		return t_ownable_garrisonable_adv_object::read_version( stream, type, version - 1 );
	return t_ownable_garrisonable_adv_object::read( stream, type );
}

// ---------------------------------------------------------------
// Write to save file
// ---------------------------------------------------------------
bool t_adv_garrison::write( std::streambuf& stream) const
{
	// Currently nothing but the base class info
	return t_ownable_garrisonable_adv_object::write( stream );
}
// ---------------------------------------------------------------
// Army visits garrison
// ---------------------------------------------------------------
void t_adv_garrison::activate_trigger( t_army* army, t_adv_map_point const& point, 
									   t_direction direction, t_adventure_frame* frame )
{
	// Don't allow boats to trigger us
	// (also need to update one line in t_adventure_path_finder::search_to to change this rule)
	if (army->is_boat()) {
		return;
	}

    // If not the same owner and there's a garrison army, go into combat.
	bool enemy_owned = !same_team( army->get_owner(), get_owner() );
	if (enemy_owned)
    {
		t_adventure_map_ptr map = get_map();
		execute_script(k_garrison_scriptable_begin_combat, army);
		if (map->is_game_over())
			return;

		if (is_alive())
		{
			t_combat_context_ptr context;

			context = new t_combat_context_adv_object( army, this, point, NULL, this );
			run_combat( *context );
			return;
		} 
		else 
		{
			execute_script(k_garrison_scriptable_lose_combat, army);
			if ( map->is_game_over() )
				return;

			t_script_context_hero context_attacker(map, NULL, army ? &army->get_creatures() : NULL, army ? army->get_owner() : NULL, &get_garrison(), get_owner());
			execute_hero_script( k_hero_scriptable_win_combat, context_attacker, NULL );
			if ( map->is_game_over() )
				return;
	
			if ( !empty() )
				place_gravestone();
		}
	}

	// Visit the garrison.  We only get here if we didn't go into combat.
	// If we did go into combat, we'll get a call when combat ends (see below)
	// where we can check if we won or lost.
	visit( army, frame );
}


void t_adv_garrison::on_combat_end( t_army* army, t_combat_result result, 
									t_creature_array* losses, t_adventure_frame* frame )
{
	t_adventure_map_ptr map = get_map();

	if (defender_lost( result ))
		set_can_remove_garrison( true ); // Original garrison is gone, new garrison is normal

	if (attacker_lost( result ))
	{
		execute_script(k_garrison_scriptable_win_combat, army);
    	return;
	}
	
	execute_script(k_garrison_scriptable_lose_combat, army);
	if ( map->is_game_over() )
		return;

	if ( !empty() )
		place_gravestone();
		
	// Set garrison to this owner.
	set_owner( army->get_owner_number() );
}

void t_adv_garrison::visit( t_army* army, t_adventure_frame* frame )
{
	t_adventure_map_ptr map = get_map();

	execute_script(k_garrison_scriptable_entered, army);
	if ( map->is_game_over() )
		return;

	if ( !same_team( army->get_owner(), get_owner() ) )
	{
		// Set garrison to this owner.
		set_owner( army->get_owner_number() );
	}

	if ((army->get_owner() == NULL) || army->get_owner()->is_computer()) 
		return;

    // Show garrison window.
	run_garrison_dialog( army );
}

t_town_image_level t_adv_garrison::get_castle_level() const
{
	return k_town_image_citadel;
}

// ---------------------------------------------------------------
// Cursor for the garrison
// ---------------------------------------------------------------
t_bitmap_cursor const& t_adv_garrison::get_cursor( t_adventure_map_window const& window,
		                                   t_army const* army ) const
{
	if ( same_team( army->get_owner(), get_owner() ) )
		return window.get_activate_cursor();
    if (empty())
		return window.get_activate_cursor();
	return window.get_attack_cursor();
}

// ---------------------------------------------------------------
// Handle a left mouse button double-click on the garrison
// Bring up a split / rearrange army dialog
// ---------------------------------------------------------------
void t_adv_garrison::left_double_click( t_mouse_event const& event, 
							                   t_adventure_frame* adventure_frame )
{
	// Show garrison window if owner
	if (m_map->get_player_number() != get_owner_number())
		return;
	
	run_garrison_dialog( NULL );
}

// ---------------------------------------------------------------
// Show army info.
// ---------------------------------------------------------------
void t_adv_garrison::right_click( t_mouse_event const& event,
		                          t_adventure_frame* adventure_frame )
{
	run_army_info_window( this, adventure_frame );
}

bool t_adv_garrison::blocks_army( t_creature_array const&	army,
								  t_path_search_type		path_type ) const
{
	if (   path_type != k_path_search_standard 
		&& path_type != k_path_search_place_army
		&& path_type != k_path_search_move_army 
		&& path_type != k_path_search_caravan )
		return false;
	if (empty())
		return false;

	return !same_team( army.get_owner(), get_owner() );
}

bool t_adv_garrison::is_triggered_by( t_creature_array const& creatures ) const
{
	// enemies always trigger the garrison.
	if (!same_team( creatures.get_owner(), get_owner() ) && !empty())
		return true;

	t_army const*	army = creatures.get_army(); 

	if (army == 0)
		return true;

	// if the army has no destination, or isn't moving here, this is not a trigger.
	t_adv_map_point destination = army->get_destination();

	if (destination.column < 0)
		return true;

	t_adv_map_point location;

	if (!get_trigger_cell( location ))
		return true;

	return destination == location;
}

float t_adv_garrison::ai_activation_value_drop( t_creature_array const& army ) const
{
	if (same_team( get_owner(), army.get_owner()) || empty())
		return 0.0f;

	return ::value_lost_in_combat( &army, this, true );
}

float t_adv_garrison::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	float scale_for_aggression 
		= ai.scale_for_combat_aggression( army.get_owner(), get_owner() );

	if (scale_for_aggression == 0.0f)
		return 0.0f;
	
	float value = scale_for_aggression * value_of_combat( &army, &get_garrison() );

	if (!same_team( army.get_owner(), get_owner()) && (value >= 0.0f))
		value += 5000;
	
	return value + m_ai_data_cache->get_bounty();
}

